/*
 * TeamSpeak 3 Dice Plugin by CintasFox
 *
 * Copyright (c) 2011 CintasFox
 */

#include "DicePlugin.h"

/* Required functions */
int roleDice(int sides)
{
	double steps = ((double)RAND_MAX) / sides;
	double wurf = rand();
	return ((int)(wurf/steps)) +1;
}

void Antworten(uint64 serverConnectionHandlerID, anyID targetMode, anyID fromID, anyID toID)
{
	if (Antwort->check() == 0)
	{
		switch (targetMode)
		{
		case TextMessageTarget_CLIENT:
			anyID myID;
			// Print own client ID
			if(ts3Func.getClientID(serverConnectionHandlerID, &myID) != ERROR_ok)
			{
				ts3Func.logMessage("Error querying client ID", LogLevel_ERROR, "Plugin", serverConnectionHandlerID);
				return;
			}
			if (myID == fromID)
			{
				if(ts3Func.requestSendPrivateTextMsg(serverConnectionHandlerID, Antwort->giveAnswer(), toID, NULL) != ERROR_ok)
				{
					ts3Func.logMessage("Error requesting send text message", LogLevel_ERROR, "Plugin", serverConnectionHandlerID);
				}
				break;
			}
			else
			{
				if(ts3Func.requestSendPrivateTextMsg(serverConnectionHandlerID, Antwort->giveAnswer(), fromID, NULL) != ERROR_ok)
				{
					ts3Func.logMessage("Error requesting send text message", LogLevel_ERROR, "Plugin", serverConnectionHandlerID);
				}
				break;
			}
		case TextMessageTarget_CHANNEL:
			if(ts3Func.requestSendChannelTextMsg(serverConnectionHandlerID, Antwort->giveAnswer(), fromID, NULL) != ERROR_ok)
			{
				ts3Func.logMessage("Error requesting send text message to channel", LogLevel_ERROR, "Plugin", serverConnectionHandlerID);
			}
			break;

		default:
			printf("%s\n", Antwort->giveAnswer());
		}
	}
}

void myInvalidParameterHandler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved)
{
	throw "Fehler in Argumentlisten einer Funktion";
}

const char* ts3plugin_name() {
    return "CF Dice Roller";
}

const char* ts3plugin_version() {
    return "1.2";
}

int ts3plugin_apiVersion() {
	return 15;
}

const char* ts3plugin_author() {
    return "CintasFox";
}

const char* ts3plugin_description() {
	return "This plugin rolls dice for you and anybody else that asks.\n\nDice commands are required to begin with #.  For example, '#3d6+4,2d20-1'. \nRandom command is required to begin with rnd[max]. For example 'rnd' or 'rnd50'";
}

void ts3plugin_setFunctionPointers(const struct TS3Functions funcs) {
    ts3Func = funcs;
}

int ts3plugin_init() {
	// Show API versions
	printf("PLUGIN DICE: Client API Version: %d, Plugin API Version: %d\n", ts3Func.getAPIVersion(), ts3plugin_apiVersion());

    // init Random Generator
	time_t ltime;
	time(&ltime);
	srand((unsigned int)ltime);
    printf("DICE: init rand\n");

	// test Random
	printf("DICE: rnd(100) = %d\n", roleDice(100));

	//init AnswerStrukture
	Antwort = new Answer();
	printf("DICE: init Answer\n");
	int cs = Antwort->check();
	if (cs)
	{
		printf("DICE: Answer failure Nr.: %d\n", cs);
		free(Antwort);
		return 1;
	}
	
	Failure = new int;
	*Failure = 0;

	pNew = myInvalidParameterHandler;

    return 0;  /* 0 = success, 1 = failure */
}

void ts3plugin_shutdown() {
    printf("DICE: shutdown\n");
	if (Failure)
	{
		free(Failure);
		Failure = NULL;
	}
	if (Antwort)
	{
		free(Antwort);
		Antwort = NULL;
	}
	if (pluginID)
	{
		free(pluginID);
		pluginID = NULL;
	}
}

/* Optional functions */

/*
 * If the plugin wants to use error return codes or plugin commands, it needs to register a command ID. This function will be automatically
 * called after the plugin was initialized. This function is optional. If you don't use error return codes or plugin commands, the function
 * can be omitted.
 * Note the passed pluginID parameter is no longer valid after calling this function, so you must copy it and store it in the plugin.
 */
/*void ts3plugin_registerPluginID(const char* id)
{
	const size_t sz = strlen(id) + 1;
	pluginID = (char*)malloc(sz * sizeof(char));
	strcpy_s(pluginID, sz, id);  // The id buffer will invalidate after exiting this function 
	printf("PLUGIN: registerPluginID: %s\n", pluginID);
}*/

const char* ts3plugin_commandKeyword()
{
	return "";
}

int ts3plugin_requestAutoload()
{
	return 1;
}

/* TeamSpeak callbacks */
void ts3plugin_onConnectStatusChangeEvent(uint64 serverConnectionHandlerID, int newStatus, unsigned int errorNumber)
{
    if(newStatus == STATUS_CONNECTION_ESTABLISHED)  // connection established and we have client and channels available
	{
        anyID myID;
        // Print own client ID
        if(ts3Func.getClientID(serverConnectionHandlerID, &myID) != ERROR_ok)
		{
            ts3Func.logMessage("Error querying client ID", LogLevel_ERROR, "Plugin", serverConnectionHandlerID);
            return;
        }
		else
		{
			//open your own dice Roling Place
			if(ts3Func.requestSendPrivateTextMsg(serverConnectionHandlerID, "This is your private dice rolling area.", myID, NULL) != ERROR_ok)
			{
				ts3Func.logMessage("Error requesting send text message", LogLevel_ERROR, "Plugin", serverConnectionHandlerID);
			}
		}
    }
}

int ts3plugin_onTextMessageEvent(uint64 serverConnectionHandlerID, anyID targetMode, anyID toID, anyID fromID, const char* fromName, const char* fromUniqueIdentifier, const char* message, int ffIgnored)
{
	char *mes = (char*)calloc(1024,sizeof(char));
	try
	{
		if(ffIgnored)
		{
			return 0;
		}
		pOld = _set_invalid_parameter_handler(pNew);
		int tf = 0;
		unsigned int max = 100;
		if(sscanf_s(message, "rnd%d", &max))
		{
			Antwort->clearAnswer(128);
			char pAnt[64];
			if (sprintf_s(pAnt, 64*sizeof(char),"[COLOR=#a4a4a4]->rnd(%d) = [/COLOR][B]%d[/B]\n", max, roleDice(max)) == -1)
			{
				*Failure = 1;
				throw "Fail";
			}
			tf = Antwort->append(pAnt,1);
			if (tf)
			{
				*Failure = 100+tf;
				throw "Fail";
			}
			Antworten(serverConnectionHandlerID, targetMode, fromID, toID);
		}
		else if(sscanf_s(message, "rtd!%d", &max))
		{
			Antwort->clearAnswer(128);
			char pAnt[64];
			if (sprintf_s(pAnt, 64*sizeof(char),"[COLOR=#a4a4a4]roll d20 -> [/COLOR][B]%d[/B]\n", roleDice(20)) == -1)
			{
				*Failure = 1;
				throw "Fail";
			}
			tf = Antwort->append(pAnt,1);
			if (tf)
			{
				*Failure = 100+tf;
				throw "Fail";
			}
			Antworten(serverConnectionHandlerID, targetMode, fromID, toID);
		}
		else if(message[0]=='#')
		{
			Antwort->clearAnswer(1023);
			tf = Antwort->check();
			if (tf)
			{
				*Failure = 200+tf;
				throw "Fail";
			}
			char *mess = mes;
			char *task, *next_task;
			if(mess == NULL)
			{
				*Failure = 2;
				throw "Fail";
			}
			else if(strcpy_s( mess, 1024*sizeof(char), message))
			{
				*Failure = 3;
				throw "Fail";
			}
			else
			{
				mess += sizeof(char);
				printf("DICE: found Task: %s\n", mess);
				task=strtok_s(mess, "abcefghijklmnopqrstuvxyzABCEFGHIJKLMNOPQRSTUVXYZ, \n", &next_task);
			}
			while(task)
			{
				tf = Antwort->append("([COLOR=#a4a4a4]",0);
				if (tf)
				{
					*Failure = 100+tf;
					throw "Fail";
				}
				int addresult = 0;
				char *part, *next_part;
				part=strtok_s(task, "+\n", &next_part);
				while(part)
				{
					if (addresult)
					{
						tf = Antwort->append("+ ",0);
						if (tf)
						{
							*Failure = 100+tf;
							throw "Fail";
						}
					}
					int subresult = 0;
					char *opart, *next_opart;
					opart=strtok_s(part, "-\n", &next_opart);
					while(opart)
					{
						if (subresult)
						{
							tf = Antwort->append("- ",0);
							if (tf)
							{
								*Failure = 100+tf;
								throw "Fail";
							}
						}
						int partresult = 0;
						int Würfel = 6;
						if (opart[0] == 'd' || opart[0] == 'D')
						{
							char pAnt[32]= "";
							if (sscanf_s(opart, "d%d", &Würfel))
							{
								partresult += roleDice(Würfel);
							}
							else if (sscanf_s(opart, "D%d", &Würfel))
							{
								partresult += roleDice(Würfel);
							}
							else
							{
								partresult += roleDice(Würfel);
							}
							if (partresult<100000000)
							{
								if (sprintf_s(pAnt, 32*sizeof(char), "[COLOR=#156d00]%d[/COLOR]", partresult) == -1)
								{
									*Failure = 4;
									throw "Fail";
								}
								tf = Antwort->append(pAnt,0);
								if (tf)
								{
									*Failure = 100+tf;
									throw "Fail";
								}
							}
						}
						else if (opart[0] == 'w' || opart[0] == 'W')
						{
							char pAnt[32]= "";
							if (sscanf_s(opart, "w%d", &Würfel))
							{
								partresult += roleDice(Würfel);
							}
							else if (sscanf_s(opart, "W%d", &Würfel))
							{
								partresult += roleDice(Würfel);
							}
							else
							{
								partresult += roleDice(Würfel);
							}
							if (partresult<100000000)
							{
								if (sprintf_s(pAnt, 32*sizeof(char), "[COLOR=#156d00]%d[/COLOR]", partresult) == -1)
								{
									*Failure = 4;
									throw "Fail";
								}
								tf = Antwort->append(pAnt,0);
								if (tf)
								{
									*Failure = 100+tf;
									throw "Fail";
								}
							}
						}
						else
						{
							int Anzahl = 0;
							Würfel = 0;
							char *number, *next_number;
							number=strtok_s(opart, "dwDW\n", &next_number);
							while(number)
							{
								if(Anzahl)
								{
									int smalresult;
									sscanf_s(number,"%d", &Würfel);
									if(Anzahl <= 5 && Würfel <= 100)
									{
										tf = Antwort->append("( [COLOR=#71d25b]",0);
										if (tf)
										{
											*Failure = 100+tf;
											throw "Fail";
										}
									}
									if(Anzahl < 10000 && Würfel < 10000)
									{
										for(int i = 0; i<Anzahl; i++)
										{
											smalresult = roleDice(Würfel);
											partresult += smalresult;
											if(Anzahl <= 5 && Würfel <= 100)
											{
												if (smalresult<1000000)
												{
													char pAnt[8];
													if (sprintf_s(pAnt, 8*sizeof(char), "%d ", smalresult) == -1)
													{
														*Failure = 4;
														throw "Fail";
													}
													tf = Antwort->append(pAnt,0);
													if (tf)
													{
														*Failure = 100+tf;
														throw "Fail";
													}
												}
											}
										}
									}
									else
									{
										tf = Antwort->append("sure thing ;)",0);
										if (tf)
										{
											*Failure = 100+tf;
											throw "Fail";
										}
									}
									if(Anzahl <= 5 && Würfel <= 100)
									{
										char pAnt[64];
										if (sprintf_s(pAnt, 64*sizeof(char), "[/COLOR])= [COLOR=#156d00]%d[/COLOR] ", partresult) == -1)
										{
											*Failure = 4;
											throw "Fail";
										}
										tf = Antwort->append(pAnt,0);
										if (tf)
										{
											*Failure = 100+tf;
											throw "Fail";
										}
									}
									else
									{
										char pAnt[32];
										if (partresult < 10000000)
										{
											if (sprintf_s(pAnt, 32*sizeof(char), "[COLOR=#156d00]%d[/COLOR] ", partresult) == -1)
											{
												*Failure = 4;
												throw "Fail";
											}
											tf = Antwort->append(pAnt,0);
											if (tf)
											{
												*Failure = 100+tf;
												throw "Fail";
											}
										}
									}
								}
								else
								{
									sscanf_s(number,"%d", &Anzahl);
								}
								number=strtok_s(NULL, "dwDW\n", &next_number);
							}
							if(Würfel == 0)
							{
								char pAnt[32];
								partresult += Anzahl;
								if (Anzahl < 10000000)
								{
									if (sprintf_s(pAnt, 32*sizeof(char), "[COLOR=#156d00]%d[/COLOR] ", Anzahl) == -1)
									{
										*Failure = 4;
										throw "Fail";
									}
									tf = Antwort->append(pAnt,0);
									if (tf)
									{
										*Failure = 100+tf;
										throw "Fail";
									}
								}
							}
						}
						if (subresult)
						{
							subresult -= partresult;
						}
						else
						{
							subresult += partresult;
						}
						opart = strtok_s(NULL, "-\n",&next_opart);
					}
					addresult += subresult;
					part = strtok_s(NULL, "+\n",&next_part);
				}
				tf = Antwort->append("[/COLOR]) = ",0);
				if (tf)
				{
					*Failure = 100+tf;
					throw "Fail";
				}
				if (addresult < 0)
				{
					char pAnt[64];
					if (sprintf_s(pAnt, 64*sizeof(char), "[COLOR=#ff0000][B]%d[/B][/COLOR] ", addresult) == -1)
					{
						*Failure = 4;
						throw "Fail";
					}
					tf = Antwort->append(pAnt,1);
					if (tf)
					{
						*Failure = 100+tf;
						throw "Fail";
					}
				}
				else
				{
					char pAnt[32];
					if (sprintf_s(pAnt, 32*sizeof(char), "[B]%d[/B] ", addresult) == -1)
					{
						*Failure = 4;
						throw "Fail";
					}
					tf = Antwort->append(pAnt,1);
					if (tf)
					{
						*Failure = 100+tf;
						throw "Fail";
					}
				}
				tf = Antwort->append(" | ",1);
				if (tf)
				{
					*Failure = 100+tf;
					throw "Fail";
				}
				task = strtok_s(NULL, "abcefghijklmnopqrstuvxyzABCEFGHIJKLMNOPQRSTUVXYZ, \n",&next_task);
			}
			Antworten(serverConnectionHandlerID, targetMode, fromID, toID);
		}
	}
	catch(char* str)
	{
		if (*Failure)
		{
			switch(*Failure)
			{
			case 1:
				ts3Func.logMessage("DICE: Fehler beim schreiben der Antwort!", LogLevel_ERROR, "Plugin", serverConnectionHandlerID);
				break;
				
			case 2:
				ts3Func.logMessage("DICE: Fehler in Antwortmodul", LogLevel_ERROR, "Plugin", serverConnectionHandlerID);
				break;

			case 3:
				ts3Func.logMessage("DICE: Fehler beim kopieren der Nachricht zur Bearbeitung!", LogLevel_ERROR, "Plugin", serverConnectionHandlerID);
				break;

			case 4:
				ts3Func.logMessage("DICE: Fehler beim Schreiben eines Teilergebnisses!", LogLevel_ERROR, "Plugin", serverConnectionHandlerID);
				break;

			case 101:
				ts3Func.logMessage("DICE: Append: no String!", LogLevel_ERROR, "Plugin", serverConnectionHandlerID);
				break;

			case 102:
				ts3Func.logMessage("DICE: Append: Kopieren des Strings nicht möglich!", LogLevel_ERROR, "Plugin", serverConnectionHandlerID);
				break;

			case 103:
				ts3Func.logMessage("DICE: Append: Anfügen des Strings nicht möglich!", LogLevel_ERROR, "Plugin", serverConnectionHandlerID);
				break;

			case 104:
				ts3Func.logMessage("DICE: Append: Ausgabe auch im gefilterten Modus zu lang!", LogLevel_ERROR, "Plugin", serverConnectionHandlerID);
				break;

			case 201:
				ts3Func.logMessage("DICE: Check: String konnte nicht reserviert werden!", LogLevel_ERROR, "Plugin", serverConnectionHandlerID);
				break;

			case 202:
				ts3Func.logMessage("DICE: Check: Länge des Strings ist 0!", LogLevel_ERROR, "Plugin", serverConnectionHandlerID);
				break;

			case 203:
				ts3Func.logMessage("DICE: Check: Stringlänge überschreitet Speicherreservierung!", LogLevel_ERROR, "Plugin", serverConnectionHandlerID);
				break;

			default:
				ts3Func.logMessage("DICE: unknown Failure!", LogLevel_ERROR, "Plugin", serverConnectionHandlerID);
				break;
			}
		}
		else
		{
			printf("DICE:Failure: %s", str);
			ts3Func.logMessage("Fail in Arguments", LogLevel_ERROR, "Plugin", serverConnectionHandlerID);
		}
		Antwort->clearAnswer(1000);
		int tf = Antwort->append("[COLOR=#ff0000][B]Fail in calculation! Please enter the causing Task in my Bugreport Page.[/B][/COLOR]\n",1);
		if (!tf)
		{
			Antworten(serverConnectionHandlerID, targetMode, fromID, toID);
		}
	}
	if (mes)
	{
		free (mes);
	}
	pNew = _set_invalid_parameter_handler(pOld);
	return 0;  // 0 = handle normally, 1 = client will ignore the text message
}


